﻿namespace Blog.Tests
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Transactions;
    using Blog.Data;
    using Blog.WebAPI.Models;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Newtonsoft.Json;

    [TestClass]
    public class PostsControllerIntegrationTests
    {
        private static string sessionKey = string.Empty;

        private static TransactionScope scope;

        [TestInitialize]
        public void TestInitialize()
        {
            scope = new TransactionScope();

            var user = new UserRegisterModel()
            {
                Username = "ValidUsername",
                DisplayName = "Valid Display Name",
                AuthCode = new string('x', 40)
            };

            // Add new user
            var httpServer = new InMemoryHttpServer("http://localhost/");
            var response = httpServer.CreatePostRequest("api/users/register", user);
            var content = response.Content.ReadAsStringAsync().Result;
            var model = JsonConvert.DeserializeObject<UserLoggedModel>(content);

            sessionKey = model.SessionKey;
        }

        [TestCleanup]
        public void TestCleanup()
        {
            scope.Dispose();
        }

        #region Create Post
        [TestMethod]
        public void CreatePost_WhenPostModelIsValidContainingTags_ShouldSaveToDatabaseAndReturnCreated()
        {
            var post = new PostCreateNewModel()
            {
                Title = "Some Integration Testing Post",
                Text = "Integration tests are easy",
                Tags = new string[] { "exam" }
            };

            var httpServer = new InMemoryHttpServer("http://localhost/");
            var response = httpServer.CreatePostRequest("api/posts?sessionKey=" + sessionKey, post);

            Assert.AreEqual(HttpStatusCode.Created, response.StatusCode);
            Assert.IsNotNull(response.Content);

            var content = response.Content.ReadAsStringAsync().Result;
            var model = JsonConvert.DeserializeObject<PostCreatedModel>(content);

            Assert.AreEqual(post.Title, model.Title);
            Assert.IsNotNull(model.Id);
        }

        [TestMethod]
        public void CreatePost_WhenPostModelIsValidWithoutTags_ShouldSaveToDatabaseAndReturnCreated()
        {
            var post = new PostCreateNewModel()
            {
                Title = "Some Integration Testing Post",
                Text = "Integration tests are easy"
            };

            var httpServer = new InMemoryHttpServer("http://localhost/");
            var response = httpServer.CreatePostRequest("api/posts?sessionKey=" + sessionKey, post);

            Assert.AreEqual(HttpStatusCode.Created, response.StatusCode);
            Assert.IsNotNull(response.Content);

            var content = response.Content.ReadAsStringAsync().Result;
            var model = JsonConvert.DeserializeObject<PostCreatedModel>(content);

            Assert.AreEqual(post.Title, model.Title);
            Assert.IsNotNull(model.Id);
        }

        [TestMethod]
        public void CreatePost_WhenPostModelIsValidBuSessionKeyIsInvalid_ShouldReturnBadRequest()
        {
            var post = new PostCreateNewModel()
            {
                Title = "Some Integration Testing Post",
                Text = "Integration tests are easy"
            };

            var httpServer = new InMemoryHttpServer("http://localhost/");
            var response = httpServer.CreatePostRequest("api/posts?sessionKey=" + new string('*', 40), post);

            Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);
        }

        [TestMethod]
        public void CreatePost_WhenPostModelWithMissingTitle_ShouldReturnBadRequest()
        {
            var post = new PostCreateNewModel()
            {
                Text = "Integration tests are easy"
            };

            var httpServer = new InMemoryHttpServer("http://localhost/");
            var response = httpServer.CreatePostRequest("api/posts?sessionKey=" + sessionKey, post);

            Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);
        }

        [TestMethod]
        public void CreatePost_WhenPostModelWithMissingText_ShouldReturnBadRequest()
        {
            var post = new PostCreateNewModel()
            {
                Title = "Some Integration Testing Post"
            };

            var httpServer = new InMemoryHttpServer("http://localhost/");
            var response = httpServer.CreatePostRequest("api/posts?sessionKey=" + sessionKey, post);

            Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);
        }

        [TestMethod]
        public void CreatePost_WhenPostModelWithEmptyTitle_ShouldReturnBadRequest()
        {
            var post = new PostCreateNewModel()
            {
                Title = string.Empty,
                Text = "Integration tests are easy"
            };

            var httpServer = new InMemoryHttpServer("http://localhost/");
            var response = httpServer.CreatePostRequest("api/posts?sessionKey=" + sessionKey, post);

            Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);
        }

        [TestMethod]
        public void CreatePost_WhenPostModelWithEmptyText_ShouldReturnBadRequest()
        {
            var post = new PostCreateNewModel()
            {
                Title = "Some Integration Testing Post",
                Text = string.Empty
            };

            var httpServer = new InMemoryHttpServer("http://localhost/");
            var response = httpServer.CreatePostRequest("api/posts?sessionKey=" + sessionKey, post);

            Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);
        } 
        #endregion

        #region Posts by Tag
        [TestMethod]
        public void SearchByTag_SearchByTwoTags_ShouldReturnOnlyPostsContainingBothTagsAndOKStatusCode()
        {
            var firstPost = new PostCreateNewModel()
            {
                Title = "Second post containing a single additional tag (except the title ones)",
                Text = "Integration tests are easy",
                Tags = new string[] { "exam" }
            };

            var secondPost = new PostCreateNewModel()
            {
                Title = "Second post containing a single additional tag (except the title ones)",
                Text = "Integration tests are easy",
                Tags = new string[] { "academy" }
            };

            var thirdPost = new PostCreateNewModel()
            {
                Title = "Third post containng both tags",
                Text = "Integration tests are easy",
                Tags = new string[] { "exam", "academy" }
            };

            var httpServer = new InMemoryHttpServer("http://localhost/");
            httpServer.CreatePostRequest("api/posts?sessionKey=" + sessionKey, firstPost);
            httpServer.CreatePostRequest("api/posts?sessionKey=" + sessionKey, secondPost);
            httpServer.CreatePostRequest("api/posts?sessionKey=" + sessionKey, thirdPost);

            var response = httpServer.CreateGetRequest("api/posts?tags=eXaM,aCaDemY&sessionKey=" + sessionKey);

            Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
            Assert.IsNotNull(response.Content);

            var content = response.Content.ReadAsStringAsync().Result;
            var models = JsonConvert.DeserializeObject<List<PostCreatedModel>>(content);

            Assert.AreEqual(1, models.Count);
            Assert.AreEqual(thirdPost.Title, models[0].Title);
        }

        [TestMethod]
        public void SearchByTag_SearchByNotExistingTag_ShouldReturnEmptyCollectionAndOKStatusCode()
        {
            var post = new PostCreateNewModel()
            {
                Title = "Some Integration Testing Post",
                Text = "Integration tests are easy"
            };

            var httpServer = new InMemoryHttpServer("http://localhost/");
            httpServer.CreatePostRequest("api/posts?sessionKey=" + sessionKey, post);

            var response = httpServer.CreateGetRequest("api/posts?tags=NonExistingTag,academy&sessionKey=" + sessionKey);

            Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
            Assert.IsNotNull(response.Content);

            var content = response.Content.ReadAsStringAsync().Result;
            var models = JsonConvert.DeserializeObject<List<PostCreatedModel>>(content);

            Assert.AreEqual(0, models.Count);
        }

        [TestMethod]
        public void SearchByTag_SearchByTagFromPostTitle_ShouldReturnEmptyCollectionAndOKStatusCode()
        {
            var post = new PostCreateNewModel()
            {
                Title = "Some Integration Testing Post (mySecretTag)",
                Text = "Integration tests are easy"
            };

            var httpServer = new InMemoryHttpServer("http://localhost/");
            httpServer.CreatePostRequest("api/posts?sessionKey=" + sessionKey, post);

            var response = httpServer.CreateGetRequest("api/posts?tags=MYSECRETTAG&sessionKey=" + sessionKey);

            Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
            Assert.IsNotNull(response.Content);

            var content = response.Content.ReadAsStringAsync().Result;
            var models = JsonConvert.DeserializeObject<List<PostCreatedModel>>(content);

            Assert.AreEqual(1, models.Count);
            Assert.AreEqual(post.Title, models[0].Title);
        }

        [TestMethod]
        public void SearchByTag_SearchByNullValueTag_ShouldReturnBadRequest()
        {
            var httpServer = new InMemoryHttpServer("http://localhost/");
            var response = httpServer.CreateGetRequest("api/posts?tags=&sessionKey=" + sessionKey);

            Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);
        }
        #endregion

        #region Leave Comment
        [TestMethod]
        public void LeaveComment_WhenCommentIsValidAndChecking_ShouldSaveToDatabaseAndReturnCreated()
        {
            // SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
            var post = new PostCreateNewModel()
            {
                Title = "LoremIpsumDolorSitAmet",
                Text = "Integration tests are easy"
            };

            // Add new post
            var httpServer = new InMemoryHttpServer("http://localhost/");
            var addPostResponse = httpServer.CreatePostRequest("api/posts?sessionKey=" + sessionKey, post);

            // Get it
            var getEmptyPostResponse = httpServer.CreateGetRequest("api/posts?tags=" + post.Title + "&sessionKey=" + sessionKey);
            Assert.AreEqual(HttpStatusCode.OK, getEmptyPostResponse.StatusCode);
            Assert.IsNotNull(getEmptyPostResponse.Content);

            // Check number of comments
            var emptyModelContent = getEmptyPostResponse.Content.ReadAsStringAsync().Result;
            var emptyModels = JsonConvert.DeserializeObject<List<PostModel>>(emptyModelContent);
            var emptyModel = emptyModels[0];
            Assert.AreEqual(0, emptyModel.Comments.Count());

            // Add a comment
            var comment = new CommentCreateModel()
            {
                Text = "Lorem Ipsum Comment"
            };

            var addCommentResponse = httpServer.CreatePutRequest("api/posts/" + emptyModel.Id + "/comment?sessionKey=" + sessionKey, comment);
            Assert.AreEqual(HttpStatusCode.OK, addCommentResponse.StatusCode);

            // Get it again
            var getCommentedPostResponse = httpServer.CreateGetRequest("api/posts?tags=" + post.Title + "&sessionKey=" + sessionKey);
            Assert.AreEqual(HttpStatusCode.OK, getCommentedPostResponse.StatusCode);
            Assert.IsNotNull(getCommentedPostResponse.Content);

            // Check number of comments again
            var commentedModelContent = getCommentedPostResponse.Content.ReadAsStringAsync().Result;
            var commentedModels = JsonConvert.DeserializeObject<List<PostModel>>(commentedModelContent);
            var commentedModel = commentedModels[0];
            Assert.AreEqual(1, commentedModel.Comments.Count());
        }

        [TestMethod]
        public void LeaveComment_WhenCommentWhenTextIsNull_ShouldReturnBadRequest()
        {
            // SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
            var post = new PostCreateNewModel()
            {
                Title = "LoremIpsumDolorSitAmet",
                Text = "Integration tests are easy"
            };

            // Add new post
            var httpServer = new InMemoryHttpServer("http://localhost/");
            var addPostResponse = httpServer.CreatePostRequest("api/posts?sessionKey=" + sessionKey, post);

            // Get it
            var getEmptyPostResponse = httpServer.CreateGetRequest("api/posts?tags=" + post.Title + "&sessionKey=" + sessionKey);
            Assert.AreEqual(HttpStatusCode.OK, getEmptyPostResponse.StatusCode);
            Assert.IsNotNull(getEmptyPostResponse.Content);

            // Check number of comments
            var emptyModelContent = getEmptyPostResponse.Content.ReadAsStringAsync().Result;
            var emptyModels = JsonConvert.DeserializeObject<List<PostModel>>(emptyModelContent);
            var emptyModel = emptyModels[0];
            Assert.AreEqual(0, emptyModel.Comments.Count());

            // Add a comment
            var comment = new CommentCreateModel()
            {
                Text = null
            };

            var addCommentResponse = httpServer.CreatePutRequest("api/posts/" + emptyModel.Id + "/comment?sessionKey=" + sessionKey, comment);
            Assert.AreEqual(HttpStatusCode.BadRequest, addCommentResponse.StatusCode);
        }

        [TestMethod]
        public void LeaveComment_WhenCommentWhenTextIsEmpty_ShouldReturnBadRequest()
        {
            // SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
            var post = new PostCreateNewModel()
            {
                Title = "LoremIpsumDolorSitAmet",
                Text = "Integration tests are easy"
            };

            // Add new post
            var httpServer = new InMemoryHttpServer("http://localhost/");
            var addPostResponse = httpServer.CreatePostRequest("api/posts?sessionKey=" + sessionKey, post);

            // Get it
            var getEmptyPostResponse = httpServer.CreateGetRequest("api/posts?tags=" + post.Title + "&sessionKey=" + sessionKey);
            Assert.AreEqual(HttpStatusCode.OK, getEmptyPostResponse.StatusCode);
            Assert.IsNotNull(getEmptyPostResponse.Content);

            // Check number of comments
            var emptyModelContent = getEmptyPostResponse.Content.ReadAsStringAsync().Result;
            var emptyModels = JsonConvert.DeserializeObject<List<PostModel>>(emptyModelContent);
            var emptyModel = emptyModels[0];
            Assert.AreEqual(0, emptyModel.Comments.Count());

            // Add a comment
            var comment = new CommentCreateModel()
            {
                Text = string.Empty
            };

            var addCommentResponse = httpServer.CreatePutRequest("api/posts/" + emptyModel.Id + "/comment?sessionKey=" + sessionKey, comment);
            Assert.AreEqual(HttpStatusCode.BadRequest, addCommentResponse.StatusCode);
        }

        [TestMethod]
        public void LeaveComment_WhenCommentIsValidButPostNotExist_ShouldReturnBadRequest()
        {
            var comment = new CommentCreateModel()
            {
                Text = "Lorem Ipsum Comment"
            };

            var httpServer = new InMemoryHttpServer("http://localhost/");
            var addCommentResponse = httpServer.CreatePutRequest("api/posts/" + 1000 + "/comment?sessionKey=" + sessionKey, comment);
            Assert.AreEqual(HttpStatusCode.BadRequest, addCommentResponse.StatusCode);
        }
        #endregion
    }
}
